﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text.RegularExpressions;
using System.Web.Mvc.Html;
using System.Text;
using DotaHelp.Models;
using System.Web.WebPages;
using System.Web.Routing;
using DotaHelp.Helpers;
using System.Linq.Expressions;
using System.Web.Mvc.Ajax;
using System.Web.Caching;

namespace System.Web.Mvc
{
    public static class Helper
    {
        private static string[] validTags = new string[] { "b", "u", "i", "s" };
		private static Smile[] smiles;

        public static MvcHtmlString ReplaceKeyOnContent<T>(this HtmlHelper<T> helper, string content, bool replaceBr = false
            , bool needEncode = true
            , bool needReplaceDiv = false
            , bool needLastDiv = true)
        {
            var cache = helper.ViewContext.HttpContext.Cache;
            var cacheKey = replaceBr + " " + needEncode + " " + needReplaceDiv + " " + content;
            var cacheRow = cache.Get(cacheKey);
            if (cacheRow != null)
            {
                return (MvcHtmlString)cacheRow;
            }

            if (smiles == null)
			{
				using (var m = new DotaModel())
				{
					smiles = smiles ?? new DotaModel().Smiles.OrderByDescending(s=>s.Name.Length).ToArray();
				}
			}

            if (needEncode)
            {
                content = HttpUtility.HtmlEncode(content);


                foreach (var t in validTags)
                {
                    content = content.Replace("&lt;" + t + "&gt;", "<" + t + ">");
                    content = content.Replace("&lt;/" + t + "&gt;", "</" + t + ">");                    
                }
								
				foreach (var s in smiles)
				{
					var ignores = Regex.Matches(content, @"(\[.*?\]|\{.*?\}|\<img.*?/>)").Cast<Match>().Select(m => new { from = m.Index, to = m.Index + m.Length }).ToArray();
					content = Regex.Replace(content, "(" + Regex.Escape(s.Name) + ")"
						, new MatchEvaluator(m =>
						{
							if (ignores.Any(p => p.from <=  m.Index &&  m.Index <= p.to))
							{
								return m.Value;
							}
							else
							{
								return "<img src='" + s.Url + "' />";
							}
						}));
				}
            }
             

            if (replaceBr)
            {
                content = content.Replace("\n", "<br/>");
            }

            var patterns = new[] {
                new {symbol='@', controller="Purchases", action = "One"},
                new {symbol='i', controller="Items", action = "One"},
                new {symbol='h', controller="Heroes", action = "One"},
				new {symbol='H', controller="Heroes", action = "OneGif"},
                new {symbol='s', controller="Skills", action = "One"},
                new {symbol='u', controller="Upgrades", action = "One"}
            };


            foreach (var p in patterns)
            {
                content = Regex.Replace(content, @"([^<a-zA-Z0-9/]|^)(?<g>" + p.symbol + "[0-9]+)", new MatchEvaluator(m =>
                {
                    var r = helper.Action(p.action, p.controller, new { id = int.Parse(m.Groups["g"].Value.Trim(p.symbol)) });
                    return m.Groups[1].Value + r;
                }));
            }

            foreach (Match img in Regex.Matches(content, @"\[(?<l>l)?(?<url>http://.+?)(\|(?<alt>.+?))?\]"))
            {
				if (img.Groups["url"].Value.StartsWith("http://www.youtube.com/", StringComparison.CurrentCultureIgnoreCase))
				{
					content = content.Replace(img.Value, string.Format(@"<object style='height: 390px; width: 640px'><param name='movie' value='http://www.youtube.com/v/{0}?version=3&feature=player_detailpage'><param name='allowFullScreen' value='true'><param name='allowScriptAccess' value='always'><embed src='http://www.youtube.com/v/{0}?version=3&feature=player_detailpage' type='application/x-shockwave-flash' allowfullscreen='true' allowScriptAccess='always' width='640' height='360'></object>"
						, HttpUtility.ParseQueryString(new Uri(img.Groups["url"].Value).Query)["v"])); 
				}
				else
				{
					content = content.Replace(img.Value, "<a class='NonUnderLineLink' href='" + HttpUtility.UrlPathEncode(img.Groups["url"].Value) + "'>"
						+ "<img class='normalImage' " + (img.Groups["l"].Success ? "style='float:left;padding:5px'" : "style='max-width:64px;max-height:64px'")
						+ " src='" + HttpUtility.UrlPathEncode(img.Groups["url"].Value) + "' "
						+ (!string.IsNullOrWhiteSpace(img.Groups["alt"].Value)?"alt='" + HttpUtility.HtmlAttributeEncode(img.Groups["alt"].Value) + "'":"")
						+ " />"
						+ "</a>");
				}
            }

            foreach (Match url in Regex.Matches(content, @"\{(?<url>http://.+?)\}"))
            {
				if (url.Groups["url"].Value.StartsWith("http://www.youtube.com/", StringComparison.CurrentCultureIgnoreCase))
				{
					content = content.Replace(url.Value, string.Format(@"<object style='height: 390px; width: 640px'><param name='movie' value='http://www.youtube.com/v/{0}?version=3&feature=player_detailpage'><param name='allowFullScreen' value='true'><param name='allowScriptAccess' value='always'><embed src='http://www.youtube.com/v/{0}?version=3&feature=player_detailpage' type='application/x-shockwave-flash' allowfullscreen='true' allowScriptAccess='always' width='640' height='360'></object>"
						, HttpUtility.ParseQueryString(new Uri(url.Groups["url"].Value).Query)["v"]));
				}
				else
				{
					content = content.Replace(url.Value,
						"<a href='" + HttpUtility.UrlPathEncode(url.Groups["url"].Value) + "'>" + url.Groups["url"].Value + "</a>");
				}
            }
            // &lt;poll&gt;59080810_f15b584591ca91344c&lt;/poll&gt;
            content = Regex.Replace(content, "&lt;poll&gt;(?<id>[a-zA-Z_0-9]*?)&lt;/poll&gt;", m => string.Format(@"
                    <div id=""vk_poll{0}""></div>
                    <script type=""text/javascript"">VK.Widgets.Poll(""vk_poll{0}"", {{width: ""500""}}, ""{0}"");
                    </script>", m.Groups["id"].Value));


            if (!needEncode && needReplaceDiv)
            {
                content = Regex.Replace(content, "<div.*?>", "<br/>", RegexOptions.IgnoreCase);
                content = Regex.Replace(content, "</div>", "", RegexOptions.IgnoreCase);
            }

            if (needLastDiv)
            {
                content += "<div style='clear:left'></div>";
            }
            var result = new MvcHtmlString(content);
            cache.Add(cacheKey, result, null, Cache.NoAbsoluteExpiration, new TimeSpan(1, 0, 0), CacheItemPriority.Normal, null);
            return result;
        }

        public static MvcHtmlString LinkImage(this HtmlHelper html, 
            string action, 
            string controller, 
            int id, 
            int imageId, 
            object htmlAttributes = null, 
            string titleController = null, 
            bool isSmall = false,
            string alt = null,
            bool strike = false,
            int? width = null,
            int? height = null)
        {
            var urlhelper = new UrlHelper(html.ViewContext.RequestContext);

            return new Tag("a", new
            {
                attrs = htmlAttributes,
                href = urlhelper.Action(action, controller, new { id }),
                style = "text-decoration: none",
				target = "_top",
                innerHtml = html.Image(imageId, titleController ?? controller, id, isSmall:isSmall, alt:alt, strike:strike,  height:height).ToHtmlString()                
            }).ToMvcString();
        }

        public static MvcHtmlString Image(this HtmlHelper html, 
            int imageId, 
            string controller, 
            int id, 
            object htmlAttributes = null, 
            bool isSmall = false,
            string alt = null,
            bool strike = false,
            int? width = null,
            int? height = null)
        {
            var urlhelper = new UrlHelper(html.ViewContext.RequestContext);

            return new Tag("img", new
            {
                attrs = htmlAttributes,
				alt = alt,
                style = "margin:2px; border: none 0px white" + (isSmall ? ";width:32px;height:32px" : "")
                    + (width != null ? ";width:" + width.Value + "px" : "")
                    + (height != null ? ";height:" + height.Value + "px" : ""),
                src = urlhelper.Image(imageId, strike),
                TitleUrl = urlhelper.Action("Title", controller, new { id }),
                @class = "TitleImg",

            }).ToMvcString();
        }


        public static MvcHtmlString LinkOnHero(this HtmlHelper html, 
            Hero hero, 
            bool isSmall = false,
            bool strike = false, 
            object htmlAttributes = null,
            int? width = null,
            int? height = null)
        {
            return html.LinkImage("Details",
                "Heroes",
                hero.Id,
                hero.AvatarImageId,
                isSmall: isSmall,
                alt: "Дота гайд по герою на dotahelp - " + hero.Name + " " + hero.Nickname,
                strike: strike,
                htmlAttributes: htmlAttributes,
                width: isSmall ? (int?)null : width,
                height: isSmall ? (int?)null : width );
        }

        public static MvcHtmlString AnimatedLinkOnHero(this HtmlHelper html,
            Hero hero,
            bool isSmall = false,
            bool strike = false,
            object htmlAttributes = null,
            int? width = null,
            int? height = null)
        {
            return html.LinkImage("Details",
                "Heroes",
                hero.Id,
                hero.AnimatedAvatarImageId,
                isSmall: isSmall,
                alt: "Анимация героя на dotahelp - " + hero.Name + " " + hero.Nickname,
                strike: strike,
                htmlAttributes: htmlAttributes,
                width: isSmall ? (int?)null : (width ?? 100),
                height: isSmall ? (int?)null : (width ?? 100));
        }

        public static MvcHtmlString LinkOnItem(this HtmlHelper html, 
            Item item, 
            object htmlAttributes = null, 
            bool isSmall = false,
            bool isRecipe = false,
            int? width = null,
            int? height = null)
        {
            return html.LinkImage("Details",
                "Items",
                item.Id,
                !isRecipe ? item.ImageId : 346,
                htmlAttributes,
                isSmall: isSmall,
                alt: "Дота гайд по предмету на dotahelp - " + item.Name + " " + item.Nickname,
                width: isSmall ? (int?)null : (width ?? 64),
                height: isSmall ? (int?)null : (width ?? 64));
        }

        public static MvcHtmlString LinkOnSkill(this HtmlHelper html,
            Skill skill,
            object htmlAttributes = null,
            bool isSmall = false,
            bool isRecipe = false,
            int? width = null,
            int? height = null)
        {
            return html.LinkImage("Details",
                "Skills",
                skill.Id,				
                !isRecipe ? skill.ImageId : 346,
                htmlAttributes,
                isSmall: isSmall,   
             	alt: "Дота гайд по способности героя на dotahelp - " + skill.Name + " " + skill.Nickname,
                width: isSmall ? (int?)null : (width ?? 64),
                height: isSmall ? (int?)null : (width ?? 64));
        }


        public static MvcHtmlString ByGroup<T>(this HtmlHelper html, string[,] groups, IEnumerable<T> list, Func<T, string> groupSelector, Func<T, MvcHtmlString> template)
        {
            var itemsByGroup = list.GroupBy(groupSelector).ToDictionary(i => i.Key, i => i.ToArray());

            return new MvcHtmlString(new HelperResult(writer =>
                {
                    writer.WriteLine("<table border=0 style='margin-left:auto; margin-right:auto;'>");

                    for (var row = 0; row < groups.GetLength(0); row++)
                    {
                        writer.WriteLine("<tr>");

                        for (var col = 0; col < groups.GetLength(1); col++)
                        {
                            writer.WriteLine("<td style='text-align:left;vertical-align:top;'>");
							writer.WriteLine("<div style='text-align:center;color:gray;margin-top:15px'>{0}</div>\r\n", groups[row, col]);
                            foreach (var item in itemsByGroup[groups[row, col]])
                            {
                                writer.WriteLine(template(item)) ;                                 
                            }
                            writer.WriteLine("</td>");
                        }
                        writer.WriteLine("</tr>");
                    }
                    writer.WriteLine("</table>");
                }).ToHtmlString());           
        }

        //public static MvcHtmlString HeroByGroup(this HtmlHelper html, IEnumerable<Hero> heroes, Func<Hero, MvcHtmlString> template)
        //{
        //    var groups = new string[,]{
        //        {"Sentinel Strength","Scourge Strength"},
        //        {"Sentinel Agility","Scourge Agility"},
        //        {"Sentinel Intelligence","Scourge Intelligence"}				
        //    };

        //    /*var groups2 = new string[,]{
        //        {"Свет - Интелект","Тьма - Интелект"},
        //        {"Свет - ловкость","Тьма - ловкость"},
        //        {"Свет - сила","Тьма - сила"}
        //    };

        //    return MvcHtmlString.Create("<h2>Дота 1</h2>" + html.ByGroup(groups, heroes, h => h.Clan, template).ToString() + 
        //        "<h2>Дота 2</h2>" + html.ByGroup(groups2, heroes, h => h.Clan, template).ToString() );*/

        //    return MvcHtmlString.Create("<h2>Все герои Dota 6.77</h2>" + html.ByGroup(groups, heroes, h => h.Clan, template).ToString());
        //}

        public static MvcHtmlString Comments(this HtmlHelper html, string title, string urlOnCurrentPage, int imageId = 346, int? top = null)
        {
            var urlhelper = new UrlHelper(html.ViewContext.RequestContext);

            return html.Action("ListForUrl", "Comments", new
            {
                url = urlOnCurrentPage,
                title = title,
                image = urlhelper.Action("Image", "Home", new { id = imageId }, "http"),
                top = top
            });
        }

        public static MvcHtmlString Raiting(this HtmlHelper html, string controller, int id, int current, string action = "Rating")
        {
            return html.Action("Rating", "Home", new { id, controller, current, action });
        }

        public static MvcHtmlString Delete(this HtmlHelper html, string controller, int id)
        {
            var ajax = new AjaxHelper(html.ViewContext, html.ViewDataContainer);

            if (html.ViewContext.RequestContext.HttpContext.User.IsInRole("Admin"))
            {
                return ajax.ActionLink("(удалить)", "Delete", new { id }, new AjaxOptions() { Confirm = "Удалить?", OnSuccess = "alert('Удалено. Обновитесь')" }, new { style = "margin-left:30px" });
            }

            return MvcHtmlString.Empty;
        }

        static T CacheValue<T>(this HttpContextBase context, string key, Func<T> make)
        {
            object obj = context.Cache[key];
            if (obj != null)
            {
                return (T) obj;
            }
            T val = make();
            context.Cache.Add(key, val, null, Cache.NoAbsoluteExpiration, new TimeSpan(1, 0, 0), CacheItemPriority.Normal, null);
            return val;
        }

        static readonly int CountRowOnPage = 20;

        public static IQueryable<T> ByPage<T>(this IQueryable<T> items, 
            int? page,
            string orderBy, 
            Expression<Func<T, int>> getRaiting,
            Expression<Func<T, DateTime>> getDate, 
            ViewDataDictionary ViewData,
            HttpContextBase httpContext
            )
        {
            var countRow = httpContext.CacheValue<int>("ByPage" + typeof(T).Name, () => items.Count());         

            ViewData.Add("Page", page ?? 1);
            ViewData.Add("OrderBy", orderBy);
            ViewData.Add("OrderByText", orderBy == "raiting" ? "по рейтингу" : "по дате");
            ViewData.Add("PageCount", countRow / CountRowOnPage);

            IQueryable<T> result;
            if (orderBy == "raiting")
            {
                result = items.OrderByDescending(getRaiting);
            }
            else
            {
                result = items.OrderByDescending(getDate);
            }

            return result.Skip(((int)(page ?? 1) - 1) * CountRowOnPage).Take(CountRowOnPage); 
        }

        public static MvcHtmlString PageNumbers(this HtmlHelper html)
        {
            var route = html.ViewContext.Controller.ValueProvider;


            var pageCount = (int)(html.ViewData["PageCount"] ?? 0);
            if (pageCount > 0)
            {
                return new MvcHtmlString(new HelperResult(writer =>
                    {
                        for (var p = 1; p <= pageCount + 1; p++)
                        {
                            if (p != (int) html.ViewData["Page"])
                            {
                                writer.WriteLine(html.ActionLink(p.ToString(),
                                    (string)route.GetValue("action").RawValue,
                                    (string)route.GetValue("controller").RawValue,
                                new
                                {
                                    orderBy = html.ViewData["OrderBy"],
                                    page = p
                                }, new { @class = "pageNumber" }));
                            }
                            else
                            {
                                writer.WriteLine("<span class='pageNumber'>" + p + "</span>");
                            }
                        }
                    }).ToHtmlString());
            }

            return MvcHtmlString.Empty;
        }

        public static HashSet<string> TablesWhichVipCanEdit = new HashSet<string>(StringComparer.CurrentCultureIgnoreCase)
        {
            "Heroes|Parameters",
			"Heroes|Description",
            "Items|Bonus",
            "Items|Price",
            "Skills|Description",
            "Skills|Nickname",
            "Skills|Parameters",
			"PurchaseItemRules|Text",
			"PurchaseHeroRules|Text"
        };

		public static HashSet<string> TablesWhichVipCanEditOnlyHis = new HashSet<string>(StringComparer.CurrentCultureIgnoreCase)
        {           
			"Comments|Text",
			"Upgrades|Comment"
        };

        public static MvcHtmlString EditDialog(this HtmlHelper html, string tableName, string fieldName, int id, string value, string author = null, bool needHtmlEditor = false)
        {
			var hc = html.ViewContext.RequestContext.HttpContext;
			var url = new UrlHelper(html.ViewContext.RequestContext);
			bool canEdit = hc.User.IsInRole("Admin")
				|| (hc.Request.IsAuthenticated && TablesWhichVipCanEdit.Contains(tableName + "|" + fieldName))
				|| (author != null && hc.Request.IsAuthenticated && TablesWhichVipCanEditOnlyHis.Contains(tableName + "|" + fieldName) && ("_vip_" + hc.User.Identity.Name).Equals(author, StringComparison.CurrentCultureIgnoreCase) );

			if (canEdit)
            {
                var divId = tableName + "_" + fieldName + "_" + id;

                return new MvcHtmlString(new Tag("a", new
                {
                    href = "javascript:void(0)",
                    onclick = "javascript:$('#" + divId + @"').html('Загрузка...').dialog({autoOpen: true, modal: true, height: 600, width: 780,
						open: function(event, ui) { 
							var form = $(this);
							$(this).load('" + url.Action("EditField", "Home", new { tableName, fieldName, id }) + @"', function(){
								  " + (needHtmlEditor?"$('textarea', form).ckeditor({height:300});":"") + @"
							});
						},
						buttons: { 
							'Сохранить': function() {  $('form', this).submit(); $(this).dialog('close'); },
							'Закрыть': function() { $(this).dialog('close'); }
						}
					})",
                    innerHtml = "(редактировать)",
                    style="font-size:10px"
                }).ToString() +
                    new Tag("div", new
                    {
                        id = divId,
                        style = "display:none",                        
                    }).ToString()
                    );
            }

            return MvcHtmlString.Empty;
        }

        public static MvcHtmlString CreateUserButton(this HtmlHelper html, string userName, string password)
        {
            if (html.ViewContext.RequestContext.HttpContext.User.IsInRole("Admin"))
            {
                return html.ActionLink("(создать пользователя)", "RegisterSimple", "Account", new { userName, password }, new { style = "margin-left:15px" });
            }
            return MvcHtmlString.Empty;
        }

        public static DateTime NowByAstana(this DateTime now)
        {
            return now.AddHours(6);
        }

		public static MvcHtmlString CompactComments(this HtmlHelper html, string url)
		{
			return html.Action("CompactListForUrl2", "Comments", new { url });			
		}

        /*public static MvcHtmlString Spoiler(this HtmlHelper html, string caption, string action, string controller, object args = null)
        {
            return new MvcHtmlString;
        }*/
    }
}